Skip to content

Add AssertionResult.Failed overload that accepts an Exception#5388

Merged
thomhurst merged 7 commits intomainfrom
feature/assertion-result-exception
Apr 4, 2026
Merged

Add AssertionResult.Failed overload that accepts an Exception#5388
thomhurst merged 7 commits intomainfrom
feature/assertion-result-exception

Conversation

@thomhurst
Copy link
Copy Markdown
Owner

Summary

Closes #5381

  • Add Exception? property to AssertionResult and AssertionResult<T> with a new Failed(string, Exception?) overload
  • Thread the exception through Assertion<T>.CreateException() as an inner exception on AssertionException
  • Update all 22 catch sites across assertions (collections, lists, mapped, regex, JSON, completesWithin, waitsFor, innerExceptions) and mock assertions to pass caught exceptions through

Test plan

  • Full solution builds with 0 errors
  • Public API snapshot tests updated and passing (net10.0, net472)
  • Backward compatible — existing Failed(string) overload unchanged

Preserve original exceptions when nested/inline assertions fail by
adding an Exception? property to AssertionResult and threading it
through to AssertionException as an inner exception. Updated 22 catch
sites across assertions and mock assertions.
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is a clean, well-executed feature that solves a real pain point — assertion failures that swallow the original cause. The implementation is backward-compatible, consistently applied across all 22 catch sites, and snapshot tests are updated correctly. Good work overall.

Design Concern: Double-Wrapping in WaitsForAssertion

The most interesting edge case is in WaitsForAssertion.cs:

// lastException is an AssertionException caught from assertion.AssertAsync()
catch (AssertionException ex) { lastException = ex; }

// ...then later:
return AssertionResult.Failed(..., lastException);

// ...which in Assertion.CreateException becomes:
new AssertionException(outerMessage, lastException) // AssertionException wrapping AssertionException

This creates an AssertionException whose InnerException is another AssertionException. The outer message already includes the last error text via ExtractAssertionMessage(lastException), so the inner exception is somewhat redundant and can produce confusing stack traces.

A cleaner approach might be to attach lastException.InnerException instead — preserving the original root cause without the redundant assertion wrapping layer. Alternatively, the WaitsFor case could be treated as a special case where the inner exception isn't threaded (keep null), since the error message is already extracted and embedded in the outer message. Worth deciding intentionally.

API Asymmetry: AssertionResult<T> Missing Public Failed Overload

AssertionResult gains a public Failed(string, Exception?) overload, but AssertionResult<T>.Failed is internal:

// AssertionResult — public ✓
public static AssertionResult Failed(string message, Exception? exception) => new(false, message, exception);

// AssertionResult<T> — internal only, no exception overload
internal static AssertionResult<T> Failed(string message) => new(false, message, default);

The implicit conversion operator correctly propagates the exception, so the common case is handled. But if a custom assertion ever returns AssertionResult<T> directly (rather than implicitly converting from AssertionResult), there's no way to attach an exception. This is a minor gap but worth noting if AssertionResult<T> is part of the extensibility surface.

Minor: FailIf Has No Exception Overload

FailIf(bool condition, string message) is a convenience helper. Since exceptions are typically only available in catch blocks (where you're already constructing the result directly), this is low priority — but worth keeping in mind for consistency if the API evolves further.

What's Working Well

  • Backward compatibility is maintained cleanly with the additive overload
  • The implicit conversion AssertionResult → AssertionResult<T> correctly threads result.Exception through (line 44 in AssertionResult{T}.cs)
  • Assertion.CreateException() applies the inner exception only when present — no null safety issues
  • All catch sites consistently updated, including the mock assertions in a separate project
  • Public API snapshots updated across all target frameworks

The double-wrapping concern in WaitsForAssertion is the one item I'd recommend addressing before merge, as it's the only case where the attached exception is itself an AssertionException rather than an originating cause.

@codacy-production
Copy link
Copy Markdown

codacy-production bot commented Apr 4, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity

Metric Results
Complexity 0

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

The bare `Failed(ex.Message, ex)` calls gave no indication which group
lookup failed. Now includes "failed to get group 'name': ..." for
consistency with other catch sites.
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up Review

The second commit ("Add context to GroupAssertion error messages") is a nice improvement — the error messages are now more contextual and consistent with how other assertion failures report their location (e.g. failed to get group 'name': ... rather than the bare exception message).

Outstanding: Double-Wrapping in WaitsForAssertion Still Present

My previous concern about WaitsForAssertion has not been addressed. The lastException being threaded through is itself an AssertionException (caught on line 63), so the resulting exception chain becomes:

AssertionException: "assertion did not pass within 1000ms after N attempts. Last error: ..."
  └── InnerException: AssertionException: "expected X but was Y"
                        └── (possibly another inner exception chain)

The outer message already includes the last assertion error text via ExtractAssertionMessage, so the InnerException is a duplicate. More importantly, it's misleading — the InnerException on an exception conventionally means "this exception happened because of the inner one," but here the inner exception is the last of potentially many failures.

Two cleaner options:

Option A — Unwrap to the original cause:

return AssertionResult.Failed(
    $"assertion did not pass within ...", 
    lastException?.InnerException ?? lastException);

This preserves the originating exception (e.g. a NullReferenceException that caused the assertion to fail) without the redundant AssertionException wrapper.

Option B — Skip the inner exception for this case (keep null):

return AssertionResult.Failed(
    $"assertion did not pass within ...");  // no exception — message is self-contained

Since ExtractAssertionMessage already embeds the last error in the message, the inner exception is low-value here and can produce confusing traces.

Option A is probably better if the original inner exception (e.g. the cause of the last AssertionException) is useful for debugging. Option B is simpler and avoids confusion if the inner exception is typically another AssertionException with no originating cause.

Everything else looks good — the GroupAssertion improvement is a nice touch, and the rest of the changes remain solid.

…Passed

- Update ~110 remaining metadata.Exception guard sites across 32 files
  to pass the exception as second argument to AssertionResult.Failed()
- Cache the Passed struct in a static readonly field to avoid repeated
  allocations on the happy path
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up Review (commit 3c538ca)

The third commit is a significant expansion — propagating exceptions through the ~110 remaining metadata.Exception guard sites and adding the _passed static readonly cache. Both are good. The caching micro-optimization is the right call here (cold path is now two comparisons rather than a struct allocation).

Outstanding: WaitsForAssertion Double-Wrapping Still Present

My concern from the previous two reviews remains unaddressed. The current state of WaitsForAssertion.cs still passes lastException (an AssertionException) as the inner exception:

// lastException is caught as AssertionException (line 63)
catch (AssertionException ex) { lastException = ex; }

// ...then passed through:
return AssertionResult.Failed(
    $"assertion did not pass within {_timeout.TotalMilliseconds:F0}ms after {attemptCount} attempts. {lastErrorMessage}",
    lastException);  // ← this is itself an AssertionException

This produces a misleading chain:

AssertionException: "assertion did not pass within 1000ms after N attempts. Last error: expected X but was Y"
  └── InnerException: AssertionException: "expected X but was Y"   ← duplicate

The outer message already contains ExtractAssertionMessage(lastException), so the InnerException is pure duplication, and it's semantically wrong — InnerException conventionally means "the root cause", not "the last of N transient failures".

The fix is a one-liner:

// Option A — unwrap to the originating cause (keeps diagnostic value):
return AssertionResult.Failed(
    $"assertion did not pass within ...",
    lastException?.InnerException ?? lastException);

// Option B — omit the inner exception (message is self-contained):
return AssertionResult.Failed(
    $"assertion did not pass within ...");

New Finding: MatchIndexAssertion Inconsistency

The third commit updated MatchIndexAssertion.cs but left an inconsistency in the if (exception != null) block:

if (exception != null)
{
    if (exception is ArgumentOutOfRangeException || exception is InvalidOperationException)
    {
        return Task.FromResult(AssertionResult.Failed(exception.Message));  // exception NOT propagated
    }
    return Task.FromResult(AssertionResult.Failed($"threw {exception.GetType().Name}", exception));  // propagated ✓
}

The first path loses the exception object. Since the goal of this PR is to preserve original exceptions for debugging, this branch should also pass the exception:

return Task.FromResult(AssertionResult.Failed(exception.Message, exception));

These two exceptions (ArgumentOutOfRangeException, InvalidOperationException) represent legitimate error causes — not passing them through is inconsistent with everything else in this PR.

Summary

Concern Status
Core API design (AssertionResult.Failed(string, Exception?)) ✅ Good
Backward compatibility ✅ Maintained
~110 additional metadata.Exception sites ✅ Done in commit 3
_passed allocation optimization ✅ Nice
Implicit conversion propagation (AssertionResult<T>) ✅ Correct
WaitsForAssertion double-wrapping ❌ Still outstanding
MatchIndexAssertion inconsistency ❌ New finding

The WaitsForAssertion issue is the blocking item — it's the only place where the "inner exception" is itself an AssertionException, inverting the intent of the feature.

Fix missed evaluationException guards in ExceptionPropertyAssertions
(8 sites), ThrowsAssertion, WithInnerExceptionsAssertion, and
MatchIndexAssertion.
WaitsForAssertion catches AssertionException from repeated assertion
attempts. Passing it directly as inner exception produces a misleading
AssertionException → AssertionException chain. Unwrap to the original
root cause instead.
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: Add AssertionResult.Failed overload that accepts an Exception

The feature itself is well-motivated and the mechanical propagation across 40+ files is correct. A few observations on design and completeness:


1. Two missed propagation sites in StringAssertions.cs

Line 546 (HasLengthAssertion.CheckAsync) still uses the old overload without forwarding the exception:

// Line 546 - exception is in scope but not forwarded
return AssertionResult.Failed($"threw {exception.GetType().Name}");

Line 666 (IsMatchingRegexAssertion.CheckAsync) uses exception.Message but also omits the exception itself:

return Task.FromResult(AssertionResult.Failed(exception.Message));

These two sites were missed in the sweep. The second one also intentionally omits the exception type from the message, which is inconsistent with every other metadata.Exception guard (all of which say "threw {type.Name}").


2. AssertionResult<T> still has no public Failed(string, Exception?) overload

The non-generic AssertionResult gained public static Failed(string, Exception?), but AssertionResult<T> only has:

internal static AssertionResult<T> Failed(string message) => new(false, message, default);

The Exception property is propagated correctly through the implicit conversion operator, but any assertion that directly creates AssertionResult<T>.Failed(...) cannot attach an exception. If AssertionResult<T> is ever used in a catch site (generated assertions via [GenerateAssertion], for instance), there is no ergonomic path. Consider:

internal static AssertionResult<T> Failed(string message, Exception? exception = null)
    => new(false, message, default, exception);

3. FailIf has no exception-carrying overload

FailIf(bool condition, string message) is in the public API but has no FailIf(bool condition, string message, Exception? exception) counterpart. Any caller that wants to conditionally fail with a contextual exception must manually write condition ? Failed(msg, ex) : Passed, breaking the helper's value. The symmetric approach would be:

public static AssertionResult FailIf(bool condition, string message, Exception? exception)
    => condition ? Failed(message, exception) : Passed;

4. WaitsForAssertion unwrapping may lose the inner exception chain

lastException?.InnerException ?? lastException

WaitsForAssertion passes lastException.InnerException to avoid the AssertionException → AssertionException chain. This is reasonable, but if the original assertion itself wrapped a cause (e.g., an EqualityException that itself has an InnerException), the intermediate AssertionException is the richer object: it carries the formatted message the user wrote. Silently unwrapping one level means the inner exception in the final AssertionException is the raw comparison exception with no assertion context.

A safer approach would be to preserve the whole AssertionException as the inner exception and rely on the outer AssertionException's message (already including "Last error: …") to provide context, rather than reaching into its InnerException. If the concern is readability of the exception chain, add a helper property/method to distinguish "assertion failed" wrapping from "original cause" wrapping, rather than unwrapping by convention.


5. The _passed field initialization race for the _passedTask

private static readonly AssertionResult _passed = new(true, string.Empty);
internal static readonly Task<AssertionResult> _passedTask = Task.FromResult(Passed);

_passedTask calls the Passed property (=> _passed) at field-initializer time, which is fine because static field initializers run top-to-bottom. However, the intent of the _passed field is to cache the struct to avoid per-call allocations on the value type. Since AssertionResult is a readonly struct, returning it from a property copies the struct on every call — the cached field helps avoid the allocation of a new instance each time Passed is referenced. This is a good optimization but might benefit from a comment explaining why _passed exists (since the optimization is subtle for a readonly struct returned by value).


6. No tests added for the new Exception propagation

The PR updates public API snapshots and mentions builds pass, but there are no new test cases demonstrating that InnerException is actually populated on a thrown AssertionException. Given the breadth of catch sites changed, at least one integration test covering a representative case (e.g., a collection assertion where the evaluated delegate threw, verifying AssertionException.InnerException != null) would give confidence that the threading actually works end-to-end.


Summary

Area Verdict
Core design (AssertionResult.Exception + threading) Good — clean additive API, backward-compatible
Mechanical propagation across 40+ files Correct, with two missed sites in StringAssertions.cs
AssertionResult<T> parity Incomplete — no Failed(string, Exception?) overload
FailIf parity Missing matching overload
WaitsForAssertion unwrapping Works but may silently drop exception context
Test coverage None added for the new behavior

The foundation is solid and the direction is right. The two missed sites and the AssertionResult<T> / FailIf gaps are the most actionable items before merging.

- Fix missed propagation site in StringAssertions.cs (line 613)
- Add Failed(string, Exception?) overload to AssertionResult<T> for
  parity with non-generic AssertionResult
- Revert WaitsForAssertion unwrapping — keep the full AssertionException
  as inner exception since it carries the formatted assertion message
github-actions bot pushed a commit to IntelliTect/CodingGuidelines that referenced this pull request Apr 6, 2026
Updated [TUnit.Core](https://github.com/thomhurst/TUnit) from 1.23.7 to
1.28.7.

<details>
<summary>Release notes</summary>

_Sourced from [TUnit.Core's
releases](https://github.com/thomhurst/TUnit/releases)._

## 1.28.7

<!-- Release notes generated using configuration in .github/release.yml
at v1.28.7 -->

## What's Changed
### Other Changes
* fix: prevent StringBuilder race in console interceptor during parallel
tests by @​thomhurst in thomhurst/TUnit#5414
### Dependencies
* chore(deps): update tunit to 1.28.5 by @​thomhurst in
thomhurst/TUnit#5415


**Full Changelog**:
thomhurst/TUnit@v1.28.5...v1.28.7

## 1.28.5

<!-- Release notes generated using configuration in .github/release.yml
at v1.28.5 -->

## What's Changed
### Other Changes
* perf: eliminate redundant builds in CI pipeline by @​thomhurst in
thomhurst/TUnit#5405
* perf: eliminate store.ToArray() allocation on mock behavior execution
hot path by @​thomhurst in thomhurst/TUnit#5409
* fix: omit non-class/struct constraints on explicit interface mock
implementations by @​thomhurst in
thomhurst/TUnit#5413
### Dependencies
* chore(deps): update tunit to 1.28.0 by @​thomhurst in
thomhurst/TUnit#5406


**Full Changelog**:
thomhurst/TUnit@v1.28.0...v1.28.5

## 1.28.0

<!-- Release notes generated using configuration in .github/release.yml
at v1.28.0 -->

## What's Changed
### Other Changes
* fix: resolve build warnings in solution by @​thomhurst in
thomhurst/TUnit#5386
* Perf: Optimize MockEngine hot paths (~30-42% faster) by @​thomhurst in
thomhurst/TUnit#5391
* Move Playwright install into pipeline module by @​thomhurst in
thomhurst/TUnit#5390
* perf: optimize solution build performance by @​thomhurst in
thomhurst/TUnit#5393
* perf: defer per-class JIT via lazy test registration + parallel
resolution by @​thomhurst in
thomhurst/TUnit#5395
* Perf: Generate typed HandleCall<T1,...> overloads to eliminate
argument boxing by @​thomhurst in
thomhurst/TUnit#5399
* perf: filter generated attributes to TUnit-related types only by
@​thomhurst in thomhurst/TUnit#5402
* fix: generate valid mock class names for generic interfaces with
non-built-in type args by @​thomhurst in
thomhurst/TUnit#5404
### Dependencies
* chore(deps): update tunit to 1.27.0 by @​thomhurst in
thomhurst/TUnit#5392
* chore(deps): update dependency path-to-regexp to v8 by @​thomhurst in
thomhurst/TUnit#5378


**Full Changelog**:
thomhurst/TUnit@v1.27.0...v1.28.0

## 1.27.0

<!-- Release notes generated using configuration in .github/release.yml
at v1.27.0 -->

## What's Changed
### Other Changes
* Fix Dependabot security vulnerabilities in docs site by @​thomhurst in
thomhurst/TUnit#5372
* fix: use 0.0.0-scrubbed sentinel version in snapshot scrubber to avoid
false Dependabot alerts by @​thomhurst in
thomhurst/TUnit#5374
* Speed up Engine.Tests by removing ProcessorCount parallelism cap by
@​thomhurst in thomhurst/TUnit#5379
* ci: add concurrency groups to cancel redundant workflow runs by
@​thomhurst in thomhurst/TUnit#5373
* Add scope-aware initialization and disposal OpenTelemetry spans to
trace timeline and HTML report by @​Copilot in
thomhurst/TUnit#5339
* Add WithInnerExceptions() for fluent AggregateException assertion
chaining by @​thomhurst in thomhurst/TUnit#5380
* Drop net6.0 and net7.0 TFMs, keep net8.0+ and netstandard2.x by
@​thomhurst in thomhurst/TUnit#5387
* Remove all [Obsolete] members and migrate callers by @​thomhurst in
thomhurst/TUnit#5384
* Add AssertionResult.Failed overload that accepts an Exception by
@​thomhurst in thomhurst/TUnit#5388
### Dependencies
* chore(deps): update dependency mockolate to 2.3.0 by @​thomhurst in
thomhurst/TUnit#5370
* chore(deps): update tunit to 1.25.0 by @​thomhurst in
thomhurst/TUnit#5371
* chore(deps): update dependency minimatch to v9.0.9 by @​thomhurst in
thomhurst/TUnit#5375
* chore(deps): update dependency path-to-regexp to v0.2.5 by @​thomhurst
in thomhurst/TUnit#5376
* chore(deps): update dependency minimatch to v10 by @​thomhurst in
thomhurst/TUnit#5377
* chore(deps): update dependency picomatch to v4 by @​thomhurst in
thomhurst/TUnit#5382
* chore(deps): update dependency svgo to v4 by @​thomhurst in
thomhurst/TUnit#5383
* chore(deps): update dependency path-to-regexp to v1 [security] by
@​thomhurst in thomhurst/TUnit#5385


**Full Changelog**:
thomhurst/TUnit@v1.25.0...v1.27.0

## 1.25.0

<!-- Release notes generated using configuration in .github/release.yml
at v1.25.0 -->

## What's Changed
### Other Changes
* Fix missing `default` constraint on explicit interface implementations
with unconstrained generics by @​thomhurst in
thomhurst/TUnit#5363
* feat(mocks): add ReturnsAsync typed factory overload with method
parameters by @​thomhurst in
thomhurst/TUnit#5367
* Fix Arg.IsNull<T> and Arg.IsNotNull<T> to support nullable value types
by @​thomhurst in thomhurst/TUnit#5366
* refactor(mocks): use file-scoped types for generated implementation
details by @​thomhurst in thomhurst/TUnit#5369
* Compress HTML report JSON data and minify CSS by @​thomhurst in
thomhurst/TUnit#5368
### Dependencies
* chore(deps): update tunit to 1.24.31 by @​thomhurst in
thomhurst/TUnit#5356
* chore(deps): update dependency mockolate to 2.2.0 by @​thomhurst in
thomhurst/TUnit#5357
* chore(deps): update dependency polyfill to 9.24.1 by @​thomhurst in
thomhurst/TUnit#5365
* chore(deps): update dependency polyfill to 9.24.1 by @​thomhurst in
thomhurst/TUnit#5364


**Full Changelog**:
thomhurst/TUnit@v1.24.31...v1.25.0

## 1.24.31

<!-- Release notes generated using configuration in .github/release.yml
at v1.24.31 -->

## What's Changed
### Other Changes
* Fix Aspire 13.2.0+ timeout caused by ProjectRebuilderResource being
awaited by @​Copilot in thomhurst/TUnit#5335
* chore(deps): update dependency polyfill to 9.24.0 by @​thomhurst in
thomhurst/TUnit#5349
* Fix nullable IParsable type recognition in source generator and
analyzer by @​Copilot in thomhurst/TUnit#5354
* fix: resolve race condition in HookExecutionOrderTests by @​thomhurst
in thomhurst/TUnit#5355
* Fix MaxExternalSpansPerTest cap bypass when Activity.Parent chain is
broken by @​Copilot in thomhurst/TUnit#5352
### Dependencies
* chore(deps): update tunit to 1.24.18 by @​thomhurst in
thomhurst/TUnit#5340
* chore(deps): update dependency stackexchange.redis to 2.12.14 by
@​thomhurst in thomhurst/TUnit#5343
* chore(deps): update verify to 31.15.0 by @​thomhurst in
thomhurst/TUnit#5346
* chore(deps): update dependency polyfill to 9.24.0 by @​thomhurst in
thomhurst/TUnit#5348


**Full Changelog**:
thomhurst/TUnit@v1.24.18...v1.24.31

## 1.24.18

<!-- Release notes generated using configuration in .github/release.yml
at v1.24.18 -->

## What's Changed
### Other Changes
* feat(mocks): shorter, more readable generated mock type names by
@​thomhurst in thomhurst/TUnit#5334
* Fix DisposeAsync() ordering for nested property injection by @​Copilot
in thomhurst/TUnit#5337
### Dependencies
* chore(deps): update tunit to 1.24.13 by @​thomhurst in
thomhurst/TUnit#5331


**Full Changelog**:
thomhurst/TUnit@v1.24.13...v1.24.18

## 1.24.13

<!-- Release notes generated using configuration in .github/release.yml
at v1.24.13 -->

## What's Changed
### Other Changes
* perf(mocks): optimize MockEngine for lower allocation and faster
verification by @​thomhurst in
thomhurst/TUnit#5319
* Remove defunct `UseTestingPlatformProtocol` reference for vscode by
@​erwinkramer in thomhurst/TUnit#5328
* perf(aspnetcore): prevent thread pool starvation during parallel
WebApplicationTest server init by @​thomhurst in
thomhurst/TUnit#5329
* fix TUnit0073 for when type from from another assembly by @​SimonCropp
in thomhurst/TUnit#5322
* Fix implicit conversion operators bypassed in property injection casts
by @​Copilot in thomhurst/TUnit#5317
* fix(mocks): skip non-virtual 'new' methods when discovering mockable
members by @​thomhurst in thomhurst/TUnit#5330
* feat(mocks): IFoo.Mock() discovery with generic fallback and ORP
resolution by @​thomhurst in
thomhurst/TUnit#5327
### Dependencies
* chore(deps): update tunit to 1.24.0 by @​thomhurst in
thomhurst/TUnit#5315
* chore(deps): update aspire to 13.2.1 by @​thomhurst in
thomhurst/TUnit#5323
* chore(deps): update verify to 31.14.0 by @​thomhurst in
thomhurst/TUnit#5325

## New Contributors
* @​erwinkramer made their first contribution in
thomhurst/TUnit#5328

**Full Changelog**:
thomhurst/TUnit@v1.24.0...v1.24.13

## 1.24.0

<!-- Release notes generated using configuration in .github/release.yml
at v1.24.0 -->

## What's Changed
### Other Changes
* perf: optimize TUnit.Mocks hot paths by @​thomhurst in
thomhurst/TUnit#5304
* fix: resolve System.Memory version conflict on .NET Framework (net462)
by @​thomhurst in thomhurst/TUnit#5303
* fix: resolve CS0460/CS0122/CS0115 when mocking concrete classes from
external assemblies by @​thomhurst in
thomhurst/TUnit#5310
* feat(mocks): parameterless Returns() and ReturnsAsync() for async
methods by @​thomhurst in thomhurst/TUnit#5309
* Fix typo in NUnit manual migration guide by @​aa-ko in
thomhurst/TUnit#5312
* refactor(mocks): unify Mock.Of<T>() and Mock.OfPartial<T>() into
single API by @​thomhurst in
thomhurst/TUnit#5311
* refactor(mocks): clean up Mock API surface by @​thomhurst in
thomhurst/TUnit#5314
* refactor(mocks): remove generic/untyped overloads from public API by
@​thomhurst in thomhurst/TUnit#5313
### Dependencies
* chore(deps): update tunit to 1.23.7 by @​thomhurst in
thomhurst/TUnit#5305
* chore(deps): update dependency mockolate to 2.1.1 by @​thomhurst in
thomhurst/TUnit#5307

## New Contributors
* @​aa-ko made their first contribution in
thomhurst/TUnit#5312

**Full Changelog**:
thomhurst/TUnit@v1.23.7...v1.24.0

Commits viewable in [compare
view](thomhurst/TUnit@v1.23.7...v1.28.7).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=TUnit.Core&package-manager=nuget&previous-version=1.23.7&new-version=1.28.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add AssertionResult.Failed overload that accepts an Exception

1 participant